home *** CD-ROM | disk | FTP | other *** search
- /*
- *------------------------------------------------------------------
- *
- * $Source: /afs/net.mit.edu/dapps/project/techinfodev/src/srv_ti/RCS/server.c,v $
- * $Revision: 1.6 $
- * $Date: 92/08/28 16:09:47 $
- * $State: Exp $
- * $Author: ark $
- * $Locker: ark $
- *
- * $Log: server.c,v $
- * Revision 1.6 92/08/28 16:09:47 ark
- * Summer 1992 final version
- *
- * Revision 1.5 92/08/12 14:18:26 ark
- * New version of 'o' transaction tells what output format to use;
- * this is a hack to allow old (but broken) clients to still work.
- *
- * Revision 1.4 92/08/11 13:46:54 ark
- * Bug fixes: 'n' transaction leaving file descriptors open;
- * 'b' transaction now disallows searches on empty strings.
- *
- * Revision 1.3 92/08/06 18:44:32 ark
- * Admin command "B" no longer crashes when there are many connections.
- *
- * Revision 1.2 92/08/04 16:28:12 ark
- * Test production version 8/4/92
- *
- * Revision 1.1 92/07/22 11:09:29 ark
- * Saber loads quietly; ANSI use standardized; command line options; no behavioral changes
- *
- * Revision 1.0 92/07/10 12:34:00 ark
- * Initial revision
- *
- * Revision 1.1 91/07/15 10:40:39 thorne
- * Initial revision
- *
- *------------------------------------------------------------------
- */
-
- #ifndef lint
- #ifndef SABER
- static char *rcsid_foo_c = "$Header: /afs/net.mit.edu/dapps/project/techinfodev/src/srv_ti/RCS/server.c,v 1.6 92/08/28 16:09:47 ark Exp Locker: ark $";
- #endif
- #endif lint
-
- /*
- * server.c
- * S. Thorne
- * This is the entry point of the Techinfo server. It loads the web and gets
- * connections. It contains the main server loop.
- */
- /*
- Copyright (C) 1989 by the Massachusetts Institute of Technology
-
- Export of this software from the United States of America is assumed
- to require a specific license from the United States Government.
- It is the responsibility of any person or organization contemplating
- export to obtain such a license before exporting.
-
- WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- distribute this software and its documentation for any purpose and
- without fee is hereby granted, provided that the above copyright
- notice appear in all copies and that both that copyright notice and
- this permission notice appear in supporting documentation, and that
- the name of M.I.T. not be used in advertising or publicity pertaining
- to distribution of the software without specific, written prior
- permission. M.I.T. makes no representations about the suitability of
- this software for any purpose. It is provided "as is" without express
- or implied warranty.
-
- */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/file.h>
- #include <signal.h>
- #include <strings.h>
- #include "network.h"
- #include "inet.h"
- /*#include "web.h" */
- #include "pdb.h"
- /* #include <unistd.h> Ulriix ??? */
-
- CONN conntab[FD_SETSIZE];
-
- fd_set read_fds;
- fd_set write_fds;
- fd_set except_fds;
-
- #define sock_readable(s) FD_ISSET(s, &read_fds)
-
- #define conn_writeable(cn) FD_ISSET(conntab[cn].c_socket,&write_fds)
- #define conn_except(cn) FD_ISSET(conntab[cn].c_socket,&except_fds)
- #define conn_readable(cn) sock_readable(conntab[cn].c_socket)
- #define conn_established(cn) (conntab[cn].c_socket != -1)
- #define conn_wait_write(cn) (conntab[cn].c_ptr) /* is the connection
- waiting for data? */
- #define conn_wait_recv(cn) (conntab[cn].c_recptr) /* is the connection
- sending a file over? */
- #define conn_timedout(cn) (conntab[cn].c_flags & C_TIMEOUT)
- #define conn_provider(cn) (conntab[cn].c_flags & C_PROVIDER)
-
- #define POLL &tv
- #define BLOCK 0
-
- #define ADAY 86400 /* this should go in a .h file, but which? -- lam */
-
- static int num_connection;
- static int listen_sock;
- int PENDING_SHUTDOWN = FALSE;
- int ALT_BANNER = FALSE;
- char *shutdown_msg;
- char *alt_banner;
- short todaysdate = 0; /* will be properly initialized later */
- static int alarmwentoff;
-
- extern int datastruct_load(int unloadfirst);
- extern void save_datastruct(void);
- extern void remove_send(TOSEND *ptr);
- extern void remove_rec(TOREC *ptr);
- extern void remove_helper(WAITFORHELPER *ptr);
- extern void log_trans(char *str);
- extern void nio_rec_more(TOREC *ptr);
- extern void catch_child(void);
- extern void catch_hup(void);
-
- extern int PROV; /* the socket # of the current provider */
- char *msglist[100];
- int debug = 0; /* no debugging is default */
-
-
- void catch_alarm(void)
- {
- alarmwentoff = 1;
- }
-
-
-
- /* load the file of error messages. error file has structure of
- <error #>:<error text> */
- int
- load_msgs(void)
- {
-
- FILE *fopen(), *msgfile;
- char msgline[101];
- int pos = 0;
- int len;
-
- msgfile = fopen(MSG_FILE, "r");
- if (!msgfile) {
- perror(MSG_FILE);
- puts("Could not find the message file");
- exit(1);
- }
- while (fgets(msgline, 100, msgfile) != '\0') {
- len = strlen(msgline);
- msgline[len - 1] = '\0';
- msglist[pos] = (char *) domalloc((unsigned int) len);
- strcpy(msglist[pos],msgline);
- pos++;
- }
- fclose(msgfile);
- return 0; /* close file */
-
- }
-
- /* write the transaction to the log file */
- void
- log_session(char *line)
- {
- int dfd;
- if ((dfd = open(LOG_FILE, O_APPEND | O_CREAT | O_WRONLY, 0644)) == 0)
- printf("error logging transaction\n");
- write(dfd, line, strlen(line));
- close(dfd);
- return;
- }
-
- void
- dump_conntab(void)
- {
- int i,fd;
- char line[200];
-
- fd = open("conntab",O_CREAT|O_TRUNC|O_WRONLY, 0644);
- for (i = 0; i < FD_SETSIZE; i++) {
- if (conn_established(i)){
- sprintf(line,"conn # %d, sock = %d, hostname = %s\n",i,conntab[i].c_socket,
- conntab[i].c_hostname);
- write(fd,line,strlen(line));}
- }
- close(fd);
- }
-
- /* Close the connection on socket c, saving the web if it was connected to
- a provider.
- Note: Should there be a modified flag in the provider's socket info to
- determine whether or not we need to save the web?
- */
- void
- close_connection(int c)
- {
- time_t secs;
- char line[120];
- int rc;
-
- time(&secs);
- if (debug) {
- printf("Closing connection to %s, port %d, socket #%d.\n",
- conntab[c].c_hostname,
- conntab[c].c_portnum,
- conntab[c].c_socket);
- fflush(stdout);
- }
-
- sprintf(line,"%d %d %s %.24s, active %.1f minutes - SERVER\n",
- conntab[c].c_portnum,
- conntab[c].c_socket,
- conntab[c].c_hostname,
- ctime(&secs),
- (float) ((secs - conntab[c].c_made) /60.0));
- log_session(line);
- if (conntab[c].c_ptr != NULL)
- remove_send(conntab[c].c_ptr);
- if (conntab[c].c_recptr != NULL)
- remove_rec(conntab[c].c_recptr);
- if (conntab[c].c_hptr != NULL)
- remove_helper(conntab[c].c_hptr); /* send KILL to child */
-
- do_free(conntab[c].c_hostname);
- do_free(conntab[c].c_uid);
- do_free(conntab[c].c_type);
- FD_CLR(conntab[c].c_socket, &read_fds); /* not needed now ? */
- FD_CLR(conntab[c].c_socket, &write_fds);
- FD_CLR(conntab[c].c_socket, &except_fds);
- rc = close(conntab[c].c_socket);
- if (rc < 0) {
- sprintf(line,"Connection close failed on socket %d\n",conntab[c].c_socket);
- printf(line); fflush(stdout);
- log_session(line);
- }
- conntab[c].c_socket = -1;
- conntab[c].c_ptr = NULL;
- conntab[c].c_recptr = NULL;
- conntab[c].c_hptr = NULL;
- num_connection--;
- }
-
-
- /*
- * Find the connection using the given socket, and mark it as having
- * timed out, so the server can close it off next time around.
- */
-
- void
- sock_timeout(int sock)
- {
- int i;
-
- for (i = 0; i < FD_SETSIZE; i++) {
- if (conntab[i].c_socket == sock) {
- conntab[i].c_flags |= C_TIMEOUT;
- break;
- }
- }
- printf("Sock #%d marked for timeout termination.\n",
- conntab[i].c_socket);
- fflush(stdout);
- }
-
- void
- server_shutdown()
- {
- int i;
-
- save_datastruct(); /* do this for production only? */
- log_trans("Shuting down the server");
- puts("\nServer shutdown, closing connections:");
-
- for (i = 0; i < FD_SETSIZE; i++) /* Close Connections */
- if (conn_established(i))
- close_connection(i);
-
- close(listen_sock);
- puts("Server has shut down -- Exiting.");
- exit(0);
- }
-
- int
- hdl_connect(int c)
- {
- CONN *conn = &conntab[c];
- int quit;
-
- if (debug) {
- printf("\nHandling transaction to %s, port %d, socket #%d.\n",
- conn->c_hostname,
- conn->c_portnum,
- conn->c_socket);
- fflush(stdout);
- }
-
- quit = hdl_transact(conn);
- time(&conn->c_last);
-
- return quit;
- }
-
- void
- make_new_connection(int listen_sock)
- {
- CONN *conn;
- char *hostname,line[100],*cp;
- int i;
- time_t secs;
- time(&secs);
- for (i = 0; i < FD_SETSIZE; i++)
- if (!conn_established(i))
- break;
- if (i == FD_SETSIZE) {
- printf("TechInfo server: reached connection limit of %d.\n",
- FD_SETSIZE);
- fflush(stdout);
- return;
- }
- conn = &conntab[i];
-
- conn->c_socket = inet_accept_connection(listen_sock, &hostname,
- &conn->c_portnum);
-
- if (conn->c_socket == -1) {
- puts("accept_connection failed");
- return;
- }
- if (PENDING_SHUTDOWN) /* don't accept new connections */
- { /* send alternate message and close connection down */
- if (shutdown_msg)
- {
- write(conn->c_socket, shutdown_msg, strlen(shutdown_msg));
- write(conn->c_socket, EOM, EOM_LEN);
- }
- else
- {
- write(conn->c_socket, NAK_BANNER, strlen(NAK_BANNER));
- write(conn->c_socket, EOM, EOM_LEN);
- }
- /* possibly sleep ?? */
- close(conn->c_socket);
- conn->c_socket = -1;
- return;
- }
- conn->c_hostname = (char *) domalloc((unsigned int) strlen(hostname) + 1);
- strcpy(conn->c_hostname, hostname);
- conn->c_uid = (char *) domalloc((unsigned int) 25);
- *conn->c_uid = '\0';
- conn->c_flags = 0;
- conn->c_type = NULL;
- conn->c_last = time(&conn->c_made);
- conn->c_ptr = NULL;
- conn->c_recptr = NULL;
- conn->c_hptr = NULL;
- conn->c_trans_cnt = 0;
- /* Added 8/12/92 ark -- Set initial output format to that defined in network.h
- Change with the 'o' transaction */
- conn->c_output_fmt = DEFAULT_OUTPUT_FORMAT;
-
- num_connection++;
- if (debug) {
- printf("Accepted new connection to %s, thru port %d, over socket #%d.\n",
- conn->c_hostname,
- conn->c_portnum,
- conn->c_socket);
- fflush(stdout);
- }
- if (ALT_BANNER) {
- write(conn->c_socket, alt_banner, strlen(alt_banner));
- write(conn->c_socket, EOM, EOM_LEN);
- }
- else {
- write(conn->c_socket, BANNER, strlen(BANNER));
- write(conn->c_socket, EOM, EOM_LEN);
- }
-
- if (fcntl(conn->c_socket, F_SETFL, FNDELAY) == -1)
- perror("fcntl");
-
- /*
- * We should turn blocking on BEFORE write the banner! Those
- * writes shouldn't be direct syscalls either.
- */
- if (conn->c_type == NULL)
- cp = "unknown";
- else
- cp = conn->c_type;
- sprintf(line,"%d %d %s %s %.24s - SERVER\n",
- conn->c_portnum,
- conn->c_socket,
- conn->c_hostname,
- cp,
- ctime(&secs));
- log_session(line);
-
- }
-
- int Standalone = 1;
-
- static int i, handled;
- static time_t secs;
-
- int ActiveJobs = 0; /* Number of pending sends & recvs
- set int netio.c */
-
- static CONN *conn;
-
- extern int max_stale_time; /* in shutdown mode wait until all conn have been
- inactive for this many minutes */
- void
- server_loop(void)
- {
- int active_socks, show_connect = 0, do_shutdown;
- char line[200];
- struct timeval tv;
-
- tv.tv_sec = 0; /* For our select when it only polls */
- tv.tv_usec = 0;
-
- alarmwentoff = 1;
- signal (SIGALRM, catch_alarm);
-
- for (;;) {
- if (alarmwentoff) {
- alarmwentoff = 0;
- save_datastruct();
- alarm(60); /* set alarm for 1 minute */
- }
-
- if (PENDING_SHUTDOWN)
- do_shutdown = TRUE;
- if (!ActiveJobs)
- show_connect++;
- if (show_connect && debug && num_connection) {
- printf("\nCurrent connections:");
- fflush(stdout);
- }
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- FD_ZERO(&except_fds);
- FD_SET(listen_sock, &read_fds);
- time(&secs);
- for (i = 0; i < FD_SETSIZE; i++) {
- if (!conn_established(i))
- continue;
- conn = &conntab[i];
- if (conn_wait_write(i))
- FD_SET(conn->c_socket, &write_fds);
- else
- FD_SET(conn->c_socket, &read_fds);
- FD_SET(conn->c_socket, &except_fds);
-
- if (!show_connect)
- continue;
- if (debug) {
- printf("\n#%2d %s.%d\tactive %.1f, last %.1f",
- conn->c_socket,
- conn->c_hostname,
- conn->c_portnum,
- (float) (secs - conn->c_made) / 60.0,
- (float) (secs - conn->c_last) / 60.0);
- fflush(stdout);
- }
- if (conn->c_ptr != NULL | conn->c_recptr != NULL) {
- if (((secs - conn->c_last) / 60.0) > 30.0)
- close_connection(i); /* this conn asked for something 30 minutes
- ago and still hasn't gotten it... */
- }
- if (PENDING_SHUTDOWN)
- if (((secs - conn->c_last) / 60.0) < max_stale_time)
- do_shutdown = FALSE;
- if (((secs - conn->c_last) / 60.0) > 700.0)
- close_connection(i); /* close connections which
- timed out */
- }
-
- if (PENDING_SHUTDOWN && do_shutdown)
- server_shutdown();
- if (show_connect && debug) {
- if (num_connection) {
- printf("\n%d connection%s currently established.\n",
- num_connection, num_connection == 1 ? "" : "s");
- fflush(stdout);
- }
- /* else
- puts(" None"); */
- }
- if (debug) {
- if (ActiveJobs)
- printf("%s for activity...",
- ActiveJobs ? "Checking" : "Waiting");
- if (ActiveJobs)
- printf("(%d asleep)...", ActiveJobs);
- fflush(stdout);
- }
- show_connect = 0;
-
-
- active_socks = select(FD_SETSIZE,&read_fds, &write_fds,
- &except_fds,
- BLOCK);
-
- if (active_socks < 0) {
- if (errno != EINTR)
- perror("select");
- continue;
- }
- if (debug) {
- if (!active_socks && ActiveJobs) {
- printf("none...continuing our %d active processes...\n",
- ActiveJobs);
- continue;
- }
-
-
- printf("%d socket%s reported with new activity.\n",
- active_socks, active_socks == 1 ? "" : "s");
- fflush(stdout);
- }
- handled = 0;
-
- /*
- * First, see if we can establish any new connections. Takes
- * only first one off queue even if there are more.
- */
-
- if (sock_readable(listen_sock))
- {
- make_new_connection(listen_sock);
- handled++;
- show_connect++;
- }
-
- /*
- * Now handle requests from all connections that have sent
- * them.
- */
-
- todaysdate = time(0) / ADAY; /* outside loop; used by other modules */
-
- for (i = 0; i < FD_SETSIZE; i++) {
- if (!conn_established(i))
- continue;
- if (conn_timedout(i)) {
- if (conn_readable(i))
- handled++;
- close_connection(i), show_connect++;
- continue;
- }
- if (conn_except(i)) {
- printf("Exceptional condition on socket %d, closing connection.\n", i);
- fflush(stdout);
- handled++;
- close_connection(i);
- continue;
- }
-
- if (conn_wait_recv(i)){ /* if we're in the middle of
- getting a file */
- nio_rec_more(conntab[i].c_recptr);
- handled++;
- continue;
- }
- else if (conn_writeable(i))
- { /* finish writing */
- handled++;
- send_more(conntab[i].c_ptr);
- }
- else if (conn_readable(i))
- {
- handled++;
-
- if (hdl_connect(i) || conntab[i].c_trans_cnt > MAX_TRANS)
- {
- close_connection(i), show_connect++;
- if (conntab[i].c_trans_cnt > MAX_TRANS) {
- sprintf(line,"MAX TRANS REACHED... closing sock %d - %s\n",
- conntab[i].c_socket,conntab[i].c_hostname);
- log_session(line);
- }
- }
- continue;
- }
- }
-
- /*
- * We have a very major problem if we thought we needed to do
- * something, and nothing was done, cause this can mean we're
- * headed into an infinte loop. So we close everyone off if
- * this happens (although it never should...)
- */
-
- if (!handled) {
- fprintf(stderr, "TechInfo server: Very major client plexing problem.\n");
- for (i = 0; i < FD_SETSIZE; i++)
- if (conn_established(i))
- close_connection(i);
- }
- }
- }
-
- void
- broken_pipe()
- {
- puts("SIGPIPE - No one to read our write.");
- close_connection(i);
- }
-
- main(int argc, char **argv)
- {
-
- extern int menu;
- extern int disptype;
- int pips_port = PIPS_PORT;
-
- char c;
- int errorflag = 0, temp_port = 0;
- extern int optind;
- extern char *optarg;
-
-
- while ((c = getopt(argc, argv, "dp:")) != EOF)
- switch (c) {
- case 'd':
- debug++;
- break;
- case 'p':
- if (optarg)
- temp_port = atoi(optarg);
- break;
- default:
- errorflag++;
- }
- if (errorflag)
- {
- printf("Usage: %s [-p port] [-d]\n", argv[0]);
- exit(1);
- }
-
- if (temp_port)
- if (temp_port > 1000)
- pips_port = temp_port;
- else
- {
- puts("Must use a port number greater than 1000");
- exit(1);
- }
-
-
- menu = NOMENU;
- disptype = SIMPLE;
- setuid(SERVER_UID); /* ti_serve */
- printf("Uid set to %d\n",getuid());
- if (access(PROV_FILE, F_OK)) {
- puts("Could not find the provider file");
- exit(1);
- }
- if (access(ADMIN_FILE, F_OK)) {
- puts("Could not find the admin file");
- exit(1);
- }
- if (access(NEXTID_FILE, F_OK)) {
- puts("Could not find the next_id file");
- exit(1);
- }
- if (access(VER_FILE, F_OK)) {
- puts("Could not find the version file");
- exit(1);
- }
- if (access(SOURCE_FILE, F_OK)) {
- puts("Could not find the source file");
- exit(1);
- }
- if (access(SERVER_FILE, F_OK)) {
- puts("Could not find the server file");
- exit(1);
- }
- for (i = 0; i < FD_SETSIZE; i++)
- conntab[i].c_socket = -1;
- signal(SIGINT, server_shutdown);
- signal(SIGTERM, server_shutdown);
- signal(SIGCHLD, catch_child);
- signal(SIGHUP, catch_hup);
- SERVER = TRUE;
- printf("\n%s\n", BANNER); fflush(stdout);
- time(&secs);
- load_msgs(); /* load the message list from disk */
- printf("Started %.24s, pid = %d\n\n", ctime(&secs), getpid());
- fflush(stdout);
-
- listen_sock = inet_make_listner(pips_port);
- if (listen_sock < 0) {
- puts("Unable to accept connections!");
- exit(1);
- }
- if (!(int) datastruct_load(0)) {
- puts("Fatal error: datastruct_load failed");
- exit(1);
- }
-
- FD_ZERO(&read_fds);
- log_trans("Starting server");
- printf("TechInfo server ready.\n"); fflush(stdout);
- num_connection = 0;
- signal(SIGPIPE, broken_pipe);
- umask(022);
-
- fflush(stdout);
- server_loop();
- }
-